package de.rj.urwalking.tasks;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.HashMap;

import org.apache.commons.codec.binary.Base64;

import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.os.AsyncTask;
import android.os.Environment;
import android.os.Handler;
import android.os.Message;
import android.util.Log;
import de.rj.urwalking.R;
import de.rj.urwalking.config.Config;
import de.rj.urwalking.data.survey.Question;
import de.rj.urwalking.data.survey.Scale;
import de.rj.urwalking.data.survey.ScalePoint;
import de.rj.urwalking.db.DBExperiment;
import de.rj.urwalking.db.ReadWritePostgreSQL;
import de.rj.urwalking.util.ExperimentManager;
import de.rj.urwalking.util.Util;

public class LoadExperimentsTask extends AsyncTask<Void, String, Boolean> {

	public static final int SUCCESS = 1;
	public static final int FAILURE = 2;

	private ProgressDialog progressDialog;
	private Context context;
	private Handler handler;

	public LoadExperimentsTask(Context context, Handler handler) {
		this.context = context;
		this.handler = handler;
	}

	@Override
	protected void onPreExecute() {
		progressDialog = ProgressDialog.show(context, context.getString(R.string.please_wait),
				context.getString(R.string.loading_experiments), true, false);
	}

	@Override
	protected Boolean doInBackground(Void... params) {
		ReadWritePostgreSQL dbWrapper = new ReadWritePostgreSQL();

		// check if the app version is marked as active		
		boolean isActive = dbWrapper.checkUrwVersion(Util.getUrWalkingVersion(context));
		ExperimentManager.getInstance().setActiveVersion(isActive);
		
		// fetch all possible experiments...
		ArrayList<DBExperiment> experiments = dbWrapper.fetchExperiments();
		// ... and store them in the ExperimentManager.
		ExperimentManager.getInstance().setExperiments(experiments);

		if (experiments == null) {
			return false;
		}

		// collect questionIds for all possible experiments in a single ArrayList
		ArrayList<Integer> questionIds = new ArrayList<Integer>();
		for (DBExperiment e : experiments) {
			questionIds.addAll(e.getQuestionIds());
		}

		publishProgress(context.getString(R.string.loading_scales));
		// fetch only the scales we need for this set of questions,...
		HashMap<Integer, Scale> scales = dbWrapper.fetchScalesFor(questionIds);
		if (scales == null) {
			return false;
		}
		publishProgress(context.getString(R.string.loading_images));
		// ...load pictures, if necessary,...
		for (Scale scale : scales.values()) {
			if (scale.isPictureBased()) {
				boolean success = loadPicturesFor(scale);
				if (!success) {
					return false;
				}
			}
		}
		// ...and save the scales.
		Question.addScales(scales);

		publishProgress(context.getString(R.string.loading_questions));
		// fetch and build the actual Questions...
		HashMap<Integer, Question> questions = dbWrapper.fetchQuestions(questionIds);
		// ...and store them in the ExperimentManager.
		ExperimentManager.getInstance().addQuestions(questions);

		if (questions == null) {
			return false;
		}

		dbWrapper.closeConnection();

		return true;
	}

	@Override
	protected void onPostExecute(Boolean success) {

		progressDialog.dismiss();

		// send message to activity via handler
		Message msg = Message.obtain();
		if (success) {
			msg.what = SUCCESS;
		} else {
			showConnectionAlert();
			msg.what = FAILURE;
		}
		handler.sendMessage(msg);

	}

	@Override
	protected void onProgressUpdate(String... values) {
		progressDialog.setMessage(values[0]);
		super.onProgressUpdate(values);
	}
	
	private boolean loadPicturesFor(Scale scale) {

		for (ScalePoint scalePoint : scale.getScalePoints()) {
			int pictureId = scalePoint.getPictureId();

			// check if there's actually a picture id defined or we will run into errors
			if (pictureId != 0) {
				Bitmap bitmap = loadSinglePicture(pictureId);
				if (bitmap == null) {
					return false;
				}
				saveToFile(bitmap, pictureId);
			}
		}
		return true;

	}

	private Bitmap loadSinglePicture(int pictureId) {
		// complete url by adding the picture id
		String urlString = Config.PICTURE_URL + pictureId;

		try {

			URL url = new URL(urlString);
			URLConnection uc = url.openConnection();
			String userpass = "urw:urw"; // FIXME: cleartext password!
			String basicAuth = "Basic " + new String(new Base64().encode(userpass.getBytes()));
			uc.setRequestProperty("Authorization", basicAuth);
			InputStream in = uc.getInputStream();
			return BitmapFactory.decodeStream(in);

		} catch (IOException e) {
			Log.d(Config.TAG, "error loading image from server");
			e.printStackTrace();
		}
		return null;

	}

	private void saveToFile(Bitmap bitmap, int pictureId) {

		ByteArrayOutputStream bytes = new ByteArrayOutputStream();
		bitmap.compress(Bitmap.CompressFormat.PNG, 100, bytes);

		File path = new File(Environment.getExternalStorageDirectory(), Config.FOLDER_NAME);
		path.mkdirs();

		File f = new File(path, pictureId + ".png");
		try {
			f.createNewFile();

			// write the bytes in file
			FileOutputStream fo = new FileOutputStream(f);
			fo.write(bytes.toByteArray());
			fo.close();

		} catch (IOException e) {
			Log.d(Config.TAG, "error writing .png to external storage");
			e.printStackTrace();
		}

	}

	private void showConnectionAlert() {
		AlertDialog.Builder builder = new AlertDialog.Builder(context);
		builder.setMessage(context.getString(R.string.no_database_connection)).setCancelable(false)
				.setPositiveButton(context.getString(R.string.retry), new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int id) {
						if (Util.hasConnection(context)) {
							new LoadExperimentsTask(context, handler).execute();
						} else {
							showAlert(context.getString(R.string.no_internet_connection));
						}
					}
				}).setNegativeButton(context.getString(R.string.abort), new DialogInterface.OnClickListener() {
					public void onClick(DialogInterface dialog, int id) {
						dialog.cancel();
					}
				});
		AlertDialog alert = builder.create();
		alert.show();
	}

	private void showAlert(String message) {
		AlertDialog.Builder builder = new AlertDialog.Builder(context);
		builder.setMessage(message).setCancelable(false).setPositiveButton("Ok", new DialogInterface.OnClickListener() {
			public void onClick(DialogInterface dialog, int id) {
				dialog.cancel();
			}
		});
		AlertDialog alert = builder.create();
		alert.show();
	}
	
	

}